package lukeer.fencing;

import csproj.fencing.Move;

import java.util.ArrayList;
import java.util.Collections;
import java.util.ListIterator;

/**
 * TreeNode is a node of the generated game search tree used by the MiniMaxPlayerPruning.
 *
 * User: Luke Richardson lukeer, Wafa M'Barek wmbarek
 * Date: 02/10/12
 * Time: 16:14
 */

public class TreeNode implements csproj.fencing.Piece {
    private final Board board;
    private ListIterator<Move> it;
    private final Move move;
    private final TreeNode parent;
    private Move bestMove;
    private final int depth;
    private final int maxDepth;
    private final int nextPlayer;
    private int result;

    public TreeNode(Move move, int nextPlayer, Board board, int depth, TreeNode parent, int maxDepth, Move bestMove) {
        this.bestMove = bestMove;
        this.nextPlayer = nextPlayer;
        this.depth = depth;
        this.board = board;
        this.move = move;
        this.parent = parent;
        this.maxDepth = maxDepth;
        if (parent != null) {
            if (parent.isRoot()) this.bestMove = move;
        }
    }

    /**
     * Creates a list of moves which lead up to this game point
     *
     * @return the list of moves
     */
    public ArrayList<Move> getMoveTree() {
        ArrayList<Move> disallowedMoves = new ArrayList<Move>();
        TreeNode currentNode = this;
        while (!currentNode.isRoot()) {
            disallowedMoves.add(currentNode.getMove());
            currentNode = currentNode.getParent();
        }
        return disallowedMoves;
    }

    /**
     * Generate next child to be added the game search tree
     *
     * @return reference to TreeNode
     */
    public TreeNode generateNextChild() {
        ArrayList<Move> disallowedMoves = getMoveTree();
        Move childMove;
        do {
            if (!it.hasNext()) return null;
            childMove = it.next();
        }
        while (disallowedMoves.contains(childMove));
        if (childMove == null) return null;

        return new TreeNode(childMove, nextPlayer, board, depth + 1, this, maxDepth, bestMove);
    }

    /**
     * Return true if there are more possible playable moves
     *
     * @return true if more children can be generated
     */
    public boolean hasNextMove() {
        return it.hasNext();
    }

    /**
     * Return true if the board is in a terminal state
     *
     * @return true if board is an end game state
     */
    public boolean isTerminalState() {
        Board newBoard = new Board(board);
        ArrayList<Move> movesToAdd = new ArrayList<Move>();
        TreeNode currentNode = this;

        ArrayList<Move> moves;
        if (currentNode.isRoot()) {
            moves = MoveGenerator.generateMoves(newBoard, nextPlayer);
            it = moves.listIterator();
            return false;
        }

        do {
            movesToAdd.add(currentNode.getMove());
            currentNode = currentNode.getParent();
        } while (!currentNode.isRoot());
        Collections.reverse(movesToAdd);
        for (Move m : movesToAdd) {
            try {
                newBoard.placePiece(m);
            } catch (FencingException e) {
                System.err.println("Invalid TreeNode");
            }
        }
        newBoard.calculateResult();
        if (newBoard.getResult() != INVALID) {
            result = newBoard.getResult();
            return true;
        }
        moves = MoveGenerator.generateMoves(newBoard, nextPlayer);
        it = moves.listIterator();
        return false;
    }

    /**
     * True if current node is at maximum depth
     *
     * @return true if current node is at maximum depth
     */
    public boolean isMaxDepth() {
        return depth == maxDepth;
    }

    /**
     * Return board result
     *
     * @return BLACK, WHITE or DRAW
     */
    public int getResult() {
        return result;
    }

    /**
     * Return move object held in node
     *
     * @return move object
     */
    public Move getMove() {
        return move;
    }

    /**
     * Return board object held in node
     *
     * @return board object
     */
    public Board getBoard() {
        return board;
    }

    /**
     * Return reference to parent node
     *
     * @return parent node
     */
    TreeNode getParent() {
        return parent;
    }

    /**
     * Return true if current node is the root node
     *
     * @return true if node has no parent
     */
    boolean isRoot() {
        return parent == null;
    }

    public Move getBestMove() {
        return bestMove;
    }
}
